// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

using System;
using System.Collections.Generic;
using KnessetCore.Dbl;
using KnessetCore.Laws;
using KnessetCore.Parser;
using KnessetCore.Utils;
using log4net;

namespace KnessetCore.Members
{
	/// <summary>
	/// Represents a single knesset member.
	/// </summary>
	public class KnessetMember : IMember, ITimeView, IPrivateLawView
	{
		#region Private vatriables.
		/// <summary>
		/// Holds db id of the member.
		/// </summary>
		private readonly int id;
		/// <summary>
		/// Holds name of the member.
		/// </summary>
		private readonly string name;
		/// <summary>
		/// Log data.
		/// </summary>
		private static readonly ILog logger = LogManager.GetLogger("KnessetCore.Members");
		/// <summary>
		/// Link to image of the knesset member.
		/// </summary>
		private readonly string imgLink;
		/// <summary>
		/// Link to information page on the knesset member.
		/// </summary>
		private readonly string knessetLink;

		#endregion

		#region Constructors
		/// <summary>
		/// Create new member.
		/// </summary>
		/// <param name="id">Db id of the member. Must be positive.</param>
		/// <param name="name">Name of the member can not be null.</param>
		/// <param name="imgLink">ink to image of the knesset member.can not be null</param>
		/// <param name="knessetLink">Link to information page on the knesset member.can not be null</param>
		/// <exception cref="ArgumentOutOfRangeException">Id number must be positive.</exception>
		internal KnessetMember(int id, string name, string imgLink, string knessetLink)
		{
			logger.Debug("~~~~~Entered KnessetMember.KnessetMember");
			if (id <= 0)
			{
				throw new ArgumentOutOfRangeException("Invalid member id: " + id);
			}
			if (name == null) throw new ArgumentNullException("name");
			if (imgLink == null) throw new ArgumentNullException("imgLink");
			if (knessetLink == null) throw new ArgumentNullException("knessetLink");
			this.id = id;
			this.name = name;
			this.knessetLink = knessetLink;
			this.imgLink = imgLink;
			logger.Info(string.Format("Name: {0} ID: {1} Img: {2} Link: {3}", name, id, imgLink, knessetLink));
			logger.Debug("~~~~~Exited KnessetMember.KnessetMember");
		} 
		#endregion

		#region IMember Members

		/// <summary>
		/// Member's db id.
		/// </summary>
		public int Id
		{
			get { return id; }
		}

		/// <summary>
		/// Member's name.
		/// </summary>
		public string Name
		{
			get { return name; }
		}

		/// <summary>
		/// Contains a link to the img to display.
		/// </summary>
		public string ImgLink
		{
			get { return imgLink; }
		}

		/// <summary>
		/// Contains a link to the knesset member's web page on the knesset site.
		/// </summary>
		public string KnessetLink
		{
			get { return knessetLink; }
		}

		#endregion

		#region ITimeView Members

		/// <summary>
		/// Add working hours. The accumulated hours are for today, If you
		/// want to add to a different date use the overloaded method.
		/// </summary>
		/// <param name="span">Working hours to add to the total working hours. Can not be more than 1440 which equals 
		/// to an entire day.</param>
		/// <exception cref="KnessetMembersException">Error while adding working hours.</exception>
		/// <exception cref="ArgumentOutOfRangeException">Time span should be less than 1440.</exception>
		/// <exception cref="MemberDoesNotExistException">Member does not exist.</exception>
		public void AddWorkingHours(TimeSpan span)
		{
			if (span.Minutes > 1440)
			{
				throw new ArgumentOutOfRangeException("Time span should be less than 1440.");
			}
			// Call overloaded method.
			AddWorkingHours(span, DateTime.Today);
			
		}

		/// <summary>
		/// Get total hours accumulated between an interval.
		/// </summary>
		/// <param name="to">End of interval. Value returned includes this day.</param>
		/// <param name="from">Start of interval. Value returned includes this day.</param>
		/// <returns>Total working hours including both 'start' and 'end'</returns>
		/// <exception cref="KnessetMembersException">Error while retrieving working hours.</exception>
		/// <exception cref="ArgumentException">'end' is less than 'start' </exception>
		/// <exception cref="MemberDoesNotExistException">Member does not exist.</exception>
		public TimeSpan GetTotalHours(DateTime to, DateTime from)
		{
			logger.Debug("~~~~~Entered KnessetMember.GetTotalHours");
			logger.Info(string.Format("From: {0} To: {1}", from, to));
			// Make sure that end is more than start.
			if (to.CompareTo(from) < 0)
			{
				throw new ArgumentException("'end' is less than 'start'");
			}
			// Before retrieving hours check that member actually exists.
			int count = (int)Constants.Members.GetCountByName(Name);
			if (count == 0)
			{
				throw new MemberDoesNotExistException(ToString());
			}
			logger.Info(string.Format("Member: {0} exists.", ToString()));
			// Run query and retrieve value.
			Object obj = Constants.Hours.GetTotalHours(id, to, from);
			int minuts = 0;
			// Make sure that we have a result.
			if (obj != null)
			{
				minuts = (int) obj;
			}
			logger.Info(string.Format("Total of minutes are: {0}", minuts));
			logger.Debug("~~~~~Exited KnessetMember.GetTotalHours");
			return new TimeSpan(0, minuts, 0);
		}

		/// <summary>
		/// Add working hours.
		/// </summary>
		/// <param name="span">Working hours to add to the total working hours.</param>
		/// <param name="date">Date to which to add working hours.</param>
		/// <exception cref="KnessetMembersException">Error while adding working hours.</exception>
		/// <exception cref="MemberDoesNotExistException">Member does not exist.</exception>
		public void AddWorkingHours(TimeSpan span, DateTime date)
		{
			logger.Debug("~~~~~Entered KnessetMember.AddWorkingHours");
			logger.Info("Date to add: " + date + ", total time span: " + span + " for member " + ToString());
			// Number of minutes in one day.
			if (span.TotalMinutes > 1440)
			{
				throw new ArgumentOutOfRangeException("Time span should be less than 1440.");
			}
			// Before adding hours check that member actually exists.
			int count = (int) Constants.Members.GetCountByName(Name);
			if (count == 0)
			{
				throw new MemberDoesNotExistException(ToString());
			}
			logger.Info(string.Format("Member: {0} exists.", ToString()));
			// Check if member already has row to the current date, if so then just update otherwise add.
			count = (int)Constants.Hours.Count(Id, date);
			if (count == 1)
			{
				// Add to working hours to db.
				logger.Info("Updating existing hours row.");
				Constants.Hours.AddToAttendingHours((short)span.TotalMinutes, date, Id);
			}
			else
			{
				logger.Info("Adding new hours row.");
				Constants.Hours.Insert(Id, (short)span.TotalMinutes, date);
			}
			logger.Debug("~~~~~Exited KnessetMember.AddWorkingHours");
		}

		#endregion

		#region IPrivateLawView Members

		/// <summary>
		/// Add a private law to the laws that were legislated by this member. If this knesset
		/// member already had this law in his laws collection then this request is ignored.
		/// </summary>
		/// <param name="parsedLaw">Law to add to this member. The Member field of the Law struct
		/// is ignored, that is, even if Member is different from this member then the law will still
		/// be added.</param>
		/// <exception cref="LawException">Error while adding law to the laws table.</exception>
		public void AddPrivateLaw(ParsedLaw parsedLaw)
		{
			logger.Debug("~~~~~Entered KnessetMember.AddPrivateLaw");
			// Check that member actually exists.
			int count = (int)Constants.Members.GetCountByName(Name);
			if (count == 0)
			{
				throw new MemberDoesNotExistException(ToString());
			}
			try 
			{	//TODO Remove
				//logger.Info("Law to add: " + parsedLaw);
				//    // Db law id that will be added to the knesset member.
				//    int dlid;
				//    // Check if a law exists in the private laws table, if it doesn't then add it.
				//    if (!PrivateLaws.GetInstance().IsLawExists(parsedLaw.KnessetNumber, parsedLaw.LawID))
				//    {
				//        logger.Info("Law did not exist in Laws table...Adding it.");
				//        dlid = PrivateLaws.GetInstance().AddLaw(parsedLaw);
				//    }
				//    else
				//    {
				//        logger.Info("Law existed in table.");
				//        dlid = PrivateLaws.GetInstance().GetLaw(parsedLaw.KnessetNumber, parsedLaw.LawID);
				//    }
				//    logger.Info("DB lid is " + dlid);
				//    // Now check if law was not already associated with the knesset member.
				//    if (!IsLawLegislatedByKnessetMember(parsedLaw.KnessetNumber, parsedLaw.LawID))
				//    {
				//        Constants.ParliamentMembersPrivateLaws.Insert(Id, dlid);
				//    }
				//    else
				//    {
				//        logger.Info("Law was already associated with this knesset member.");
				//    }
			}
			catch (Exception e)
			{
				throw new LawException(e.Message,e);
			}
			logger.Debug("~~~~~Exited KnessetMember.AddPrivateLaw");

		}

		/// <summary>
		/// Get all laws that were legislated by this member.
		/// </summary>
		/// <returns>A collection contating all laws legislated by this member.</returns>
		/// <exception cref="MemberDoesNotExistException">Member does not exist.</exception>
		/// <exception cref="LawException">Error while retrieving law information.</exception>
		public ICollection<ParsedLaw> GetLaws()
		{
			logger.Debug("~~~~~Entered KnessetMember.GetLaws");
			// Check that member actually exists.
			int count = (int)Constants.Members.GetCountByName(Name);
			if (count == 0)
			{
				throw new MemberDoesNotExistException(ToString());
			}
			ICollection<ParsedLaw> returnValue = new List<ParsedLaw>();
			try
			{
				//TODO Remove
				//// Retrieve all values from table.
				//Knesset.ParliamentMembersPrivateLawsDataTable results = Constants.ParliamentMembersPrivateLaws.
				//                                                GetPrivateLawsByParliamentMemberID(id);
				//// Iterate and create laws object.
				//foreach (Knesset.ParliamentMembersPrivateLawsRow o in results)
				//{
				//    ParsedLaw parsedLaw = PrivateLaws.GetInstance().GetLaw(o.PrivateLawID);
				//    parsedLaw.Member = this;
				//    returnValue.Add(parsedLaw);
				//}
			}
			catch (Exception e)
			{
				
				throw new LawException(e.Message,e);
			}

			logger.Debug("~~~~~Exited KnessetMember.GetLaws");
			return returnValue;
		}

		/// <summary>
		/// Get total number of laws legislated by this member.
		/// </summary>
		/// <returns>Total number of laws legislated by this member.</returns>
		/// <exception cref="MemberDoesNotExistException">Member does not exist.</exception>
		/// <exception cref="LawException">Error while retrieving law information.</exception>
		public uint GetLawCount()
		{
			logger.Debug("~~~~~Entered KnessetMember.GetLawCount");
			// Check that member actually exists.
			int count = (int)Constants.Members.GetCountByName(Name);
			if (count == 0)
			{
				throw new MemberDoesNotExistException(ToString());
			}

			try
			{
				// Retrieve all values from table.
				Knesset.ParliamentMembersPrivateLawsDataTable results = Constants.ParliamentMembersPrivateLaws.
																GetPrivateLawsByParliamentMemberID(id);
				
				// count.
				logger.Info("Count is " + count);
				count = results.Count;
			}
			catch (Exception e)
			{

				throw new LawException(e.Message, e);
			}
			logger.Debug("~~~~~Exited KnessetMember.GetLawCount");
			return (uint) count;
		}

		/// <summary>
		/// Is a law was legislated by this specific knesset member.
		/// </summary>
		/// <param name="knessetNumber">The knesset number in which the law was legislated.</param>
		/// <param name="lawID">The law id as specified by the knesset site.</param>
		/// <returns>True if was legislated by this member, false otherwise.</returns>
		/// <exception cref="MemberDoesNotExistException">Member does not exist.</exception>
		/// <exception cref="LawException">Error while retrieving law information.</exception>
		public bool IsLawLegislatedByKnessetMember(uint knessetNumber, uint lawID)
		{
			logger.Debug("~~~~~Entered KnessetMember.IsLawLegislatedByKnessetMember");
			// Check that member actually exists.
			int count = (int)Constants.Members.GetCountByName(Name);
			if (count == 0)
			{
				throw new MemberDoesNotExistException(ToString());
			}

			// Get db law id.
			//TODO Remove
			//int ldbid = PrivateLaws.GetInstance().GetLawDbid(knessetNumber, lawID);
			//count = (int)Constants.ParliamentMembersPrivateLaws.GetCountByLawIDAndMemberID(Id, ldbid);
			//logger.Debug("~~~~~Exited KnessetMember.IsLawLegislatedByKnessetMember");
			return count != 0;
		}

		#endregion

		#region Object Members
		///<summary>
		///Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
		///</summary>
		///
		///<returns>
		///A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
		///</returns>
		///<filterpriority>2</filterpriority>
		public override string ToString()
		{
			return "ID: " + Id + " Name: " + Name;
		} 
		#endregion
	}
}